/*******************************************************************************************
 * Copyright (c) 2006-2009 Alex Robson, Rob Simmons, Craig Israel, and Rachel Twyford
 *
 * Use of this software in any form requires that you have both 
 * read and agreed to the following terms:
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the "Software"), 
 * to deal in the Software without restriction, including without limitation 
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions: 
 *
 * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice, 
 * this list of conditions and the following disclaimer in the 
 * documentation and/or other materials provided with the distribution.
 * Neither the name of Nvigorate nor the names of its contributors may 
 * be used to endorse or promote products derived from this software without 
 * specific prior written permission. 
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
 * DISCLAIMED. IN NO EVENT SHALL ANY CONTRIBUTOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * ****************************************************************************************/

using System;
using System.Collections.Generic;
using Nvigorate.Logging;
using Nvigorate.Relational.Mapping.Index;

namespace Nvigorate.Relational.Repository
{
    public abstract class BaseRepository : IRepository
    {
        protected IMapIndex _mapIndex;
        protected string _repositoryName = "";
        protected IRepositoryMapIndex _repositoryMapIndex;
        protected bool _enforceConcurrency = false;

        /// <summary>
        /// Indicates whether or not the repository should enforce optimistic concurrency.
        /// When true the repository will throw concurrency exceptions on a failed
        /// update attempt.
        /// </summary>
        public bool EnforceConcurrency
        {
            get { return _enforceConcurrency; }
            set { _enforceConcurrency = value; }
        }

        /// <summary>
        /// The name of the source for the data.
        /// In the case of a database, this will be the name of the connection string.
        /// </summary>
        public string RepositoryName
        {
            get { return _repositoryName; }
            set { _repositoryName = value; }
        }

        public IRepositoryMapIndex RepositoryMapIndex
        {
            get
            {
                _repositoryMapIndex = _repositoryMapIndex ?? new RepositoryMapIndex(_mapIndex, _repositoryName);
                return _repositoryMapIndex;
            }
        }

        #region Implementation of IRepository

        /// <summary>
        /// Deletes a single record from the source
        /// </summary>
        /// <typeparam name="T">The business object type which represents data in the data source</typeparam>
        /// <param name="instance">The instance which represents the record to delete</param>
        public abstract int Delete<T>(T instance) where T : class, new();

        /// <summary>
        /// Deletes a single record from the source
        /// </summary>
        /// <typeparam name="T">The business object type which represents data in the data source</typeparam>
        /// <param name="instance">The instance which represents the record to delete</param>
        /// <param name="recursive">Deletes all instances in the hierarchy recursively</param>
        public abstract int Delete<T>(T instance, bool recursive) where T : class, new();

        /// <summary>
        /// Deletes a collection of records from the source
        /// </summary>
        /// <typeparam name="T">The business object type which represents data in the data source</typeparam>
        /// <param name="collection">The collection which represents the records to delete</param>
        public abstract int Delete<T>(IEnumerable<T> collection) where T : class, new();

        /// <summary>
        /// Deletes a collection of records from the source
        /// </summary>
        /// <typeparam name="T">The business object type which represents data in the data source</typeparam>
        /// <param name="collection">The collection which represents the records to delete</param>
        /// <param name="recursive">Deletes all records in the hierarchy recursively</param>
        public abstract int Delete<T>(IEnumerable<T> collection, bool recursive) where T : class, new();

        /// <summary>
        /// Retrieves the child instance for the subject object
        /// </summary>
        /// <typeparam name="T">The business object type which represents data in the data source</typeparam>
        /// <typeparam name="S">The subject business object type</typeparam>
        /// <param name="subject">The parent instance</param>
        /// <returns>The child instance for the subject</returns>
        public abstract T GetRelative<T, S>(S subject) where T : class, new();

        /// <summary>
        /// Retrieves the child instance for the subject object
        /// </summary>
        /// <typeparam name="T">The business object type which represents data in the data source</typeparam>
        /// <typeparam name="S">The subject business object type</typeparam>
        /// <param name="subject">The parent instance</param>
        /// <param name="instructions">Specifies how records should be loaded and populated</param>
        /// <returns>The child instance for the subject</returns>
        public abstract T GetRelative<T, S>(S subject, LoadInstruction<T> instructions) where T : class, new();

        /// <summary>
        /// Gets child collection for the subject object
        /// </summary>
        /// <typeparam name="T">The business object type which represents data in the data source</typeparam>
        /// <typeparam name="S">The subject business object type</typeparam>
        /// <param name="subject">The parent instance</param>
        /// <returns>The child instance for the subject</returns>
        public abstract IList<T> GetRelatives<T, S>(S subject) where T : class, new();

        /// <summary>
        /// Gets child collection for the subject object
        /// </summary>
        /// <typeparam name="T">The business object type which represents data in the data source</typeparam>
        /// <typeparam name="S">The subject business object type</typeparam>
        /// <param name="subject">The parent instance</param>
        /// <param name="instructions">Specifies how records should be loaded and populated</param>
        /// <returns>The child instance for the subject</returns>
        public abstract IList<T> GetRelatives<T, S>(S subject, LoadInstruction<T> instructions) where T : class, new();

        /// <summary>
        /// Retrieve the first instance based on the search criteria provided
        /// </summary>
        /// <typeparam name="T">The business object type which represents data in the data source</typeparam>
        /// <param name="instructions">Specifies how records should be loaded and populated</param>
        /// <returns>The first instance matching the criteria provided</returns>
        public abstract T GetInstance<T>(LoadInstruction<T> instructions) where T : class, new();

        /// <summary>
        /// Retrieve an instance based on the search criteria provided
        /// </summary>
        /// <typeparam name="T">The business object type which represents data in the data source</typeparam>
        /// <param name="id">The primary key value of the desired instance</param>
        /// <returns>The first instance matching the criteria provided</returns>
        public abstract T GetInstance<T>(object id) where T : class, new();

        /// <summary>
        /// Retrieve an instance based on the search criteria provided
        /// </summary>
        /// <typeparam name="T">The business object type which represents data in the data source</typeparam>
        /// <param name="id">The primary key value of the desired instance</param>
        /// <param name="instructions">Specifies how records should be loaded and populated</param>
        /// <returns>The first instance matching the criteria provided</returns>
        public abstract T GetInstance<T>(object id, LoadInstruction<T> instructions) where T : class, new();

        /// <summary>
        /// Retrieve an instance based on the search criteria provided
        /// </summary>
        /// <typeparam name="T">The business object type which represents data in the data source</typeparam>
        /// <param name="keyList">Identifies the properties which uniquely identify an instance</param>
        /// <param name="ids">The values for each key to filter the record by</param>
        /// <returns>The first instance matching the criteria provided</returns>
        public abstract T GetInstance<T>(string[] keyList, object[] ids) where T : class, new();

        /// <summary>
        /// Retrieve an instance based on the search criteria provided
        /// </summary>
        /// <typeparam name="T">The business object type which represents data in the data source</typeparam>
        /// <param name="keyList">Identifies the properties which uniquely identify an instance</param>
        /// <param name="ids">The values for each key to filter the record by</param>
        /// <param name="instructions">Specifies how records should be loaded and populated</param>
        /// <returns>The first instance matching the criteria provided</returns>
        public abstract T GetInstance<T>(string[] keyList, object[] ids, LoadInstruction<T> instructions) where T : class, new();

        /// <summary>
        /// Retrieve a complete collection of instances from the data source
        /// </summary>
        /// <typeparam name="T">The business object type which represents data in the data source</typeparam>
        /// <returns>The complete collection of instances from the source</returns>
        public abstract IList<T> GetCollection<T>() where T : class, new();

        /// <summary>
        /// Retrieve a complete collection of instances from the data source
        /// </summary>
        /// <typeparam name="T">The business object type which represents data in the data source</typeparam>
        /// <param name="instructions">Specifies how records should be loaded and populated</param>
        /// <returns>The complete collection of instances from the source</returns>
        public abstract IList<T> GetCollection<T>(LoadInstruction<T> instructions) where T : class, new();

        /// <summary>
        /// Inserts or updates the record represented by the instance
        /// </summary>
        /// <typeparam name="T">The business object type which represents data in the data source</typeparam>
        /// <param name="instance">The instance to persist</param>
        public abstract int Persist<T>(T instance) where T : class, new();

        /// <summary>
        /// Saves a collection of instances by inserting or updating each one in the data source
        /// </summary>
        /// <typeparam name="T">The business object type which represents data in the data source</typeparam>
        /// <param name="collection">The collection of instances to persist</param>
        public abstract int Persist<T>(IEnumerable<T> collection) where T : class, new();

        #endregion

        protected BaseRepository()
        {
            try
            {
                if (_mapIndex == null)
                {
                    _mapIndex = new MapIndexFactory().CreateMapIndex();
                }
            }
            catch (Exception ex)
            {
                LogManager.LogError(ex, Severities.Critical, "An error occurred while attempting to load the MapIndex.");
                throw;
            }
        }
    }
}